/**
 *  ClassName: BaseDaoImpl.java
 *  created on 2014-05-23
 *  Copyrights 2011-2012 ITVK.CN All rights reserved.
 *  site: http://WWW.ITVK.CN
 *  email: qjyong@gmail.com
 */
package com.lxq.dao.impl.common;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import com.lxq.common.DbHelper;
import com.lxq.dao.common.BaseDao;

/**
 * 使用commons-dbutils.jar实现的通用Dao实现类
 * @author qiujy
 */
@SuppressWarnings({"unchecked","rawtypes"})
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
	/** 当前操作的实体的类型信息 */
	protected Class clazz;
	
	/** 实体对应的表名 */
	protected String tableName;
	
	/**SQL执行器*/
	protected QueryRunner qr = new QueryRunner();
	
	/** 负责把结果集的第一行数据封装成一个JavaBean实例的结果集处理器 */
	protected BeanHandler<T> beanHandler; 
	
	/** 将结果集中的每一行数据都封装到一个对应的JavaBean实例中，存放到List里的结果集处理器 */
	protected BeanListHandler<T> beanListHandler;
	
	/** 将结果集中某一条记录的其中某一列的数据存成 Object的结果集处理器 */
	protected ScalarHandler<Long> scalarHandler;
	
	/**
	 * 构造方法，会通过反射机制获取子类传递过来的实体类的类型信息，并自动以类名的全小写作为表名
	 */
	public BaseDaoImpl(){
		//通过反射机制获取子类传递过来的实体类的类型信息
		ParameterizedType type = (ParameterizedType)this.getClass()
				.getGenericSuperclass();
		clazz = (Class)type.getActualTypeArguments()[0];
		
		//创建结果集处理器
		beanHandler = new BeanHandler<T>(clazz);
		beanListHandler = new BeanListHandler<T>(clazz);
		scalarHandler = new ScalarHandler<Long>();
		
		//获取表名
		this.tableName = clazz.getSimpleName().toLowerCase();
	}

	/**
	 * 根据主键删除一个实例 
	 * @param pk 主键
	 */
	public void delete(Number pk) {
		Connection conn = null;
		// delete from 表名 wher 主键列名=值
		String sql = "DELETE FROM "+ getTableName() + " WHERE " + getPKName() + "=?";
		try {
			conn = DbHelper.getConn();
			qr.update(conn, sql, pk);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
	}
	
	/**
	 * 判断指定主键对应的实体是否存在 
	 * @param pk 主键值
	 * @return 存在返回true;否则返回false
	 */
	public boolean exists(Number pk){
		boolean flag = false;
		
		Connection conn = null;
		
		String sql = "SELECT COUNT(" + getPKName() + ") FROM "+ getTableName() + " WHERE " + getPKName() + "=?";
		try {
			conn = DbHelper.getConn();
			
			Long temp = (Long)qr.query(conn, sql, scalarHandler);
			if(temp != null && temp.longValue() > 0){
				flag = true;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		
		return flag;
	}

	/**
	 * 获取所有实体实例的列表
	 * @return 符合条件的实例列表
	 */
	public List<T> findAll() {
		List<T> list = null;
		
		Connection conn = null;
		//select * from 表名
		String sql = "SELECT * FROM "+ getTableName();
		try {
			conn = DbHelper.getConn();
			list = qr.query(conn, sql, beanListHandler);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		return list;
	}


	/**
	 * 根据主键获取对应的实例 
	 * @param pk 主键值
	 * @return 如果查询成功，返回符合条件的实例;如果查询失败，返回null
	 */
	public T findOne(Number pk) {
		T t = null;
		Connection conn = null;
		//select * from 表名 where 主键列名=?
		String sql = "SELECT * FROM "+ getTableName() + " WHERE " + getPKName() + "=?";
		try {
			conn = DbHelper.getConn();
			t = qr.query(conn, sql, beanHandler, pk);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		
		return t;
	}
	
	/**
	 * 统计总实体实例的数量
	 * @return 总数量
	 */
	public int count() {
		int count = 0;
		Connection conn = null;
		String sql = "SELECT count( * ) FROM "+ getTableName();
		try {
			conn = DbHelper.getConn();
			Long temp = (Long)qr.query(conn, sql, scalarHandler);
			if(temp != null){
				count = temp.intValue();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		return count;
	}
	
	/**
	 * 更新指定实例的详细信息
	 * @param entity 实例 
	 */
	public void update(T entity){
		Connection conn = null;
		List<Object> valueList = new ArrayList<Object>();
		
		//update 表名 set 列=?,列=? where 主键列名=?
		StringBuilder sql = new StringBuilder("UPDATE ");
		sql.append(getTableName());
		sql.append(" SET ");
		Map<String,Object> fieldValueMap = getFieldValueMap(entity);
		Object idValue = fieldValueMap.get(getPKName());
		fieldValueMap.remove(getPKName());
		int i = 0;
		for(Entry<String, Object> entry : fieldValueMap.entrySet()){
			if(i++ > 0){
				sql.append(",");
			}
			sql.append(entry.getKey() + "=?");
			valueList.add(entry.getValue());
		}
		sql.append(" WHERE ").append(getPKName()).append("=?");
		valueList.add(idValue);
		
		//System.out.println(sql.toString());
		//System.out.println(valueList);
		
		try {
			conn = DbHelper.getConn();
			qr.update(conn, sql.toString(), valueList.toArray());
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
	}
	
	/**
	 * 保存一个实例
	 * @param entity 要保存的实例 
	 */
	public void save(T entity){
		Connection conn = null;
		//insert into 表名(列名,列名...) values(?,?...);
		StringBuilder sql = new StringBuilder("INSERT INTO ");
		sql.append(getTableName());
		sql.append("(");
		
		StringBuilder values = new StringBuilder("(");
		List<Object> valueList = new ArrayList<Object>();
		Map<String,Object> fieldValueMap = getFieldValueMap(entity);
		
		fieldValueMap.remove(getPKName());
		
		int i = 0;
		for(Entry<String, Object> entry : fieldValueMap.entrySet()){
			if(i++ > 0){
				sql.append(",");
				values.append(",");
			}
			sql.append(entry.getKey()); //实体类的属性名作为表的列名
			values.append("?");
			valueList.add(entry.getValue());
		}
		sql.append(")");
		values.append(")");
		sql.append(" VALUES").append(values);
		
		//System.out.println(sql.toString());
		//System.out.println(valueList);
		try {
			conn = DbHelper.getConn();
			
			qr.update(conn, sql.toString(), valueList.toArray());
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
	}

	
	/**
	 * 根据指定的SQL语句和参数值执行更新数据的操作
	 * @param sql SQL语句
	 * @param paramValues 参数值数组
	 */
	public void update(String sql, Object... paramValues){
		Connection conn = null;
		try {
			conn = DbHelper.getConn();
			qr.update(conn, sql, paramValues);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
	}
	
	/**
	 * 根据指定的SQL语句和参数值执行查询数据的操作
	 * @param sql SQL语句
	 * @param paramValues 参数值数组
	 * @return 符合条件的实体对象列表
	 */
	public List<T> find(String sql, Object... paramValues){
		List<T> list = null;
		Connection conn = null;
		try {
			conn = DbHelper.getConn();
			list = qr.query(conn, sql, beanListHandler, paramValues);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		return list;
	}
	/**
	 * 根据指定的SQL语句和参数值执行单个对象的查询操作
	 * @param sql SQL语句
	 * @param paramValues 参数值数组
	 * @return 符合条件的实体对象
	 */
	public T findOne(String sql, Object... paramValues){
		T t = null;
		
		Connection conn = null;
		try {
			conn = DbHelper.getConn();
			t = qr.query(conn, sql, beanHandler, paramValues);
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			DbHelper.close(conn);
		}
		return t;
	}
	
	/**
	 * 获取该实体类对应的表名，默认为全小写的实体类名。如果需要更改，请在子类中重写本方法
	 * @return 表名
	 */
	protected String getTableName(){
		return tableName;
	}
	
	/**
	 * 获取主键列名，默认为“id”。如果需要更改，请在子类中重写本方法
	 * @return 主键列名
	 */
	protected String getPKName(){
		return "id";
	}
	
	/**
	 * 获取指定实例的所有属性名及对应值的Map实例
	 * @param entity 实例
	 * @return 字段名及对应值的Map实例
	 */
	protected Map<String, Object> getFieldValueMap(T entity){
		//key是属性名,value是对应值
		Map<String, Object> fieldValueMap = new HashMap<String, Object>();
		
		//获取当前加载的实体类中所有属性(字段)
		Field[] fields = this.clazz.getDeclaredFields(); //反射
		
		for(int i = 0; i < fields.length; i++){
			Field f = fields[i];
			String name = f.getName(); //属性名
			
			if(!"serialVersionUID".equals(name)){ //忽略序列化版本ID号
				//System.out.println("字段的名：" + name);
				
				f.setAccessible(true);//取消 Java 语言访问检查
				try {
					Object value = f.get(entity); //反射获取该属性的值
					
					fieldValueMap.put(name, value);
					
					//System.out.println("字段值：" + value);
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
		return fieldValueMap;
	}
}